/*
 * @(#)Color.java	1.75 03/12/19
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.davetrudes.jung.client.graphics;

/**
 * The <code>Color</code> class is used to encapsulate colors in the default
 * sRGB color space or colors in arbitrary color spaces identified by a
 * {@link ColorSpace}. Every color has an implicit alpha value of 1.0 or an
 * explicit one provided in the constructor. The alpha value defines the
 * transparency of a color and can be represented by a float value in the range
 * 0.0&nbsp;-&nbsp;1.0 or 0&nbsp;-&nbsp;255. An alpha value of 1.0 or 255 means
 * that the color is completely opaque and an alpha value of 0 or 0.0 means that
 * the color is completely transparent. When constructing a <code>Color</code>
 * with an explicit alpha or getting the color/alpha components of a
 * <code>Color</code>, the color components are never premultiplied by the alpha
 * component.
 * <p>
 * The default color space for the Java 2D(tm) API is sRGB, a proposed standard
 * RGB color space. For further information on sRGB, see <A
 * href="http://www.w3.org/pub/WWW/Graphics/Color/sRGB.html">
 * http://www.w3.org/pub/WWW/Graphics/Color/sRGB.html </A>.
 * <p>
 * <b>Removed functionality:</b>
 * <ul>
 * <li>
 * <code>public float[] getComponents(ColorSpace cspace, float[] compArray)</code>
 * </li>
 * <li>
 * <code>public Color(ColorSpace cspace, float components[], float alpha)</code>
 * </li>
 * <li>
 * <code>public float[] getColorComponents(ColorSpace cspace, float[] compArray)</code>
 * </li>
 * <li><code>public ColorSpace getColorSpace()</code></li>
 * <li><code>public synchronized PaintContext createContext(...)</code></li>
 * <li><code></code></li>
 * <li><code></code></li>
 * <li><code></code></li>
 * </ul>
 * </p>
 *
 * @version 10 Feb 1997
 * @author Sami Shaio
 * @author Arthur van Hoff
 * @author dave.trudes
 *
 * @see ColorSpace
 * @see AlphaComposite
 */
public class Color implements Paint {

	/**
	 * The color white. In the default sRGB space.
	 */
	public final static Color white = new Color(255, 255, 255);

	/**
	 * The color white. In the default sRGB space.
	 */
	public final static Color WHITE = white;

	/**
	 * The color light gray. In the default sRGB space.
	 */
	public final static Color lightGray = new Color(192, 192, 192);

	/**
	 * The color light gray. In the default sRGB space.
	 */
	public final static Color LIGHT_GRAY = lightGray;

	/**
	 * The color gray. In the default sRGB space.
	 */
	public final static Color gray = new Color(128, 128, 128);

	/**
	 * The color gray. In the default sRGB space.
	 */
	public final static Color GRAY = gray;

	/**
	 * The color dark gray. In the default sRGB space.
	 */
	public final static Color darkGray = new Color(64, 64, 64);

	/**
	 * The color dark gray. In the default sRGB space.
	 */
	public final static Color DARK_GRAY = darkGray;

	/**
	 * The color black. In the default sRGB space.
	 */
	public final static Color black = new Color(0, 0, 0);

	/**
	 * The color black. In the default sRGB space.
	 */
	public final static Color BLACK = black;

	/**
	 * The color red. In the default sRGB space.
	 */
	public final static Color red = new Color(255, 0, 0);

	/**
	 * The color red. In the default sRGB space.
	 */
	public final static Color RED = red;

	/**
	 * The color pink. In the default sRGB space.
	 */
	public final static Color pink = new Color(255, 175, 175);

	/**
	 * The color pink. In the default sRGB space.
	 */
	public final static Color PINK = pink;

	/**
	 * The color orange. In the default sRGB space.
	 */
	public final static Color orange = new Color(255, 200, 0);

	/**
	 * The color orange. In the default sRGB space.
	 */
	public final static Color ORANGE = orange;

	/**
	 * The color yellow. In the default sRGB space.
	 */
	public final static Color yellow = new Color(255, 255, 0);

	/**
	 * The color yellow. In the default sRGB space.
	 */
	public final static Color YELLOW = yellow;

	/**
	 * The color green. In the default sRGB space.
	 */
	public final static Color green = new Color(0, 255, 0);

	/**
	 * The color green. In the default sRGB space.
	 */
	public final static Color GREEN = green;

	/**
	 * The color magenta. In the default sRGB space.
	 */
	public final static Color magenta = new Color(255, 0, 255);

	/**
	 * The color magenta. In the default sRGB space.
	 */
	public final static Color MAGENTA = magenta;

	/**
	 * The color cyan. In the default sRGB space.
	 */
	public final static Color cyan = new Color(0, 255, 255);

	/**
	 * The color cyan. In the default sRGB space.
	 */
	public final static Color CYAN = cyan;

	/**
	 * The color blue. In the default sRGB space.
	 */
	public final static Color blue = new Color(0, 0, 255);

	/**
	 * The color blue. In the default sRGB space.
	 */
	public final static Color BLUE = blue;

	/**
	 * Private data.
	 *
	 * transient private long pData;
	 */

	/**
	 * The color value.
	 *
	 * @serial
	 * @see #getRGB
	 */
	int value;

	/**
	 * The color value in the default sRGB <code>ColorSpace</code> as
	 * <code>float</code> components (no alpha). If <code>null</code> after
	 * object construction, this must be an sRGB color constructed with 8-bit
	 * precision, so compute from the <code>int</code> color value.
	 *
	 * @serial
	 * @see #getRGBColorComponents
	 * @see #getRGBComponents
	 */
	private float frgbvalue[] = null;

	/**
	 * The color value in the native <code>ColorSpace</code> as
	 * <code>float</code> components (no alpha). If <code>null</code> after
	 * object construction, this must be an sRGB color constructed with 8-bit
	 * precision, so compute from the <code>int</code> color value.
	 *
	 * @serial
	 * @see #getRGBColorComponents
	 * @see #getRGBComponents
	 */
	private float fvalue[] = null;

	/**
	 * The alpha value as a <code>float</code> component. If
	 * <code>frgbvalue</code> is <code>null</code>, this is not valid data, so
	 * compute from the <code>int</code> color value.
	 *
	 * @serial
	 * @see #getRGBComponents
	 * @see #getComponents
	 */
	private float falpha = 0.0f;

	/**
	 * The <code>ColorSpace</code>. If <code>null</code>, then it's default is
	 * sRGB.
	 *
	 * @serial
	 * @see #getColor
	 * @see #getColorSpace
	 * @see #getColorComponents private ColorSpace cs = null;
	 */

	/*
	 * JDK 1.1 serialVersionUID
	 */
	private static final long serialVersionUID = 118526816881161077L;

	private static final double FACTOR = 0.7;

	/**
	 * Converts a <code>String</code> to an integer and returns the specified
	 * opaque <code>Color</code>. This method handles string formats that are
	 * used to represent octal and hexidecimal numbers.
	 *
	 * @param nm
	 *            a <code>String</code> that represents an opaque color as a
	 *            24-bit integer
	 * @return the new <code>Color</code> object.
	 * @see java.lang.Integer#decode
	 * @exception NumberFormatException
	 *                if the specified string cannot be interpreted as a
	 *                decimal, octal, or hexidecimal integer.
	 * @since JDK1.1
	 */
	public static Color decode(String nm) throws NumberFormatException {
		Integer intval = Integer.decode(nm);
		int i = intval.intValue();
		return new Color((i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF);
	}

	/**
	 * Finds a color in the system properties.
	 * <p>
	 * The argument is treated as the name of a system property to be obtained.
	 * The string value of this property is then interpreted as an integer which
	 * is then converted to a <code>Color</code> object.
	 * <p>
	 * If the specified property is not found or could not be parsed as an
	 * integer then <code>null</code> is returned.
	 *
	 * @param nm
	 *            the name of the color property
	 * @return the <code>Color</code> converted from the system property.
	 * @see java.lang.System#getProperty(java.lang.String)
	 * @see java.lang.Integer#getInteger(java.lang.String)
	 * @see java.awt.Color#Color(int)
	 * @since JDK1.0
	 */
	public static Color getColor(String nm) {
		return getColor(nm, null);
	}

	/**
	 * Finds a color in the system properties.
	 * <p>
	 * The first argument is treated as the name of a system property to be
	 * obtained. The string value of this property is then interpreted as an
	 * integer which is then converted to a <code>Color</code> object.
	 * <p>
	 * If the specified property is not found or cannot be parsed as an integer
	 * then the <code>Color</code> specified by the second argument is returned
	 * instead.
	 *
	 * @param nm
	 *            the name of the color property
	 * @param v
	 *            the default <code>Color</code>
	 * @return the <code>Color</code> converted from the system property, or the
	 *         specified <code>Color</code>.
	 * @see java.lang.System#getProperty(java.lang.String)
	 * @see java.lang.Integer#getInteger(java.lang.String)
	 * @see java.awt.Color#Color(int)
	 * @since JDK1.0
	 */
	public static Color getColor(String nm, Color v) {
		Integer intval = Integer.parseInt(nm);
		if (intval == null) {
			return v;
		}
		int i = intval.intValue();
		return new Color((i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF);
	}

	/**
	 * Finds a color in the system properties.
	 * <p>
	 * The first argument is treated as the name of a system property to be
	 * obtained. The string value of this property is then interpreted as an
	 * integer which is then converted to a <code>Color</code> object.
	 * <p>
	 * If the specified property is not found or could not be parsed as an
	 * integer then the integer value <code>v</code> is used instead, and is
	 * converted to a <code>Color</code> object.
	 *
	 * @param nm
	 *            the name of the color property
	 * @param v
	 *            the default color value, as an integer
	 * @return the <code>Color</code> converted from the system property or the
	 *         <code>Color</code> converted from the specified integer.
	 * @see java.lang.System#getProperty(java.lang.String)
	 * @see java.lang.Integer#getInteger(java.lang.String)
	 * @see java.awt.Color#Color(int)
	 * @since JDK1.0
	 */
	public static Color getColor(String nm, int v) {
		Integer intval = Integer.parseInt(nm);
		int i = (intval != null) ? intval.intValue() : v;
		return new Color((i >> 16) & 0xFF, (i >> 8) & 0xFF, (i >> 0) & 0xFF);
	}

	/**
	 * Creates a <code>Color</code> object based on the specified values for the
	 * HSB color model.
	 * <p>
	 * The <code>s</code> and <code>b</code> components should be floating-point
	 * values between zero and one (numbers in the range 0.0-1.0). The
	 * <code>h</code> component can be any floating-point number. The floor of
	 * this number is subtracted from it to create a fraction between 0 and 1.
	 * This fractional number is then multiplied by 360 to produce the hue angle
	 * in the HSB color model.
	 *
	 * @param h
	 *            the hue component
	 * @param s
	 *            the saturation of the color
	 * @param b
	 *            the brightness of the color
	 * @return a <code>Color</code> object with the specified hue, saturation,
	 *         and brightness.
	 * @since JDK1.0
	 */
	public static Color getHSBColor(float h, float s, float b) {
		return new Color(HSBtoRGB(h, s, b));
	}

	/**
	 * Converts the components of a color, as specified by the HSB model, to an
	 * equivalent set of values for the default RGB model.
	 * <p>
	 * The <code>saturation</code> and <code>brightness</code> components should
	 * be floating-point values between zero and one (numbers in the range
	 * 0.0-1.0). The <code>hue</code> component can be any floating-point
	 * number. The floor of this number is subtracted from it to create a
	 * fraction between 0 and 1. This fractional number is then multiplied by
	 * 360 to produce the hue angle in the HSB color model.
	 * <p>
	 * The integer that is returned by <code>HSBtoRGB</code> encodes the value
	 * of a color in bits 0-23 of an integer value that is the same format used
	 * by the method {@link #getRGB() <code>getRGB</code>}. This integer can be
	 * supplied as an argument to the <code>Color</code> constructor that takes
	 * a single integer argument.
	 *
	 * @param hue
	 *            the hue component of the color
	 * @param saturation
	 *            the saturation of the color
	 * @param brightness
	 *            the brightness of the color
	 * @return the RGB value of the color with the indicated hue, saturation,
	 *         and brightness.
	 * @see java.awt.Color#getRGB()
	 * @see java.awt.Color#Color(int)
	 * @see java.awt.image.ColorModel#getRGBdefault()
	 * @since JDK1.0
	 */
	public static int HSBtoRGB(float hue, float saturation, float brightness) {
		int r = 0, g = 0, b = 0;
		if (saturation == 0) {
			r = g = b = (int) (brightness * 255.0f + 0.5f);
		} else {
			float h = (hue - (float) Math.floor(hue)) * 6.0f;
			float f = h - (float) java.lang.Math.floor(h);
			float p = brightness * (1.0f - saturation);
			float q = brightness * (1.0f - saturation * f);
			float t = brightness * (1.0f - (saturation * (1.0f - f)));
			switch ((int) h) {
			case 0:
				r = (int) (brightness * 255.0f + 0.5f);
				g = (int) (t * 255.0f + 0.5f);
				b = (int) (p * 255.0f + 0.5f);
				break;
			case 1:
				r = (int) (q * 255.0f + 0.5f);
				g = (int) (brightness * 255.0f + 0.5f);
				b = (int) (p * 255.0f + 0.5f);
				break;
			case 2:
				r = (int) (p * 255.0f + 0.5f);
				g = (int) (brightness * 255.0f + 0.5f);
				b = (int) (t * 255.0f + 0.5f);
				break;
			case 3:
				r = (int) (p * 255.0f + 0.5f);
				g = (int) (q * 255.0f + 0.5f);
				b = (int) (brightness * 255.0f + 0.5f);
				break;
			case 4:
				r = (int) (t * 255.0f + 0.5f);
				g = (int) (p * 255.0f + 0.5f);
				b = (int) (brightness * 255.0f + 0.5f);
				break;
			case 5:
				r = (int) (brightness * 255.0f + 0.5f);
				g = (int) (p * 255.0f + 0.5f);
				b = (int) (q * 255.0f + 0.5f);
				break;
			}
		}
		return 0xff000000 | (r << 16) | (g << 8) | (b << 0);
	}

	/**
	 * Converts the components of a color, as specified by the default RGB
	 * model, to an equivalent set of values for hue, saturation, and brightness
	 * that are the three components of the HSB model.
	 * <p>
	 * If the <code>hsbvals</code> argument is <code>null</code>, then a new
	 * array is allocated to return the result. Otherwise, the method returns
	 * the array <code>hsbvals</code>, with the values put into that array.
	 *
	 * @param r
	 *            the red component of the color
	 * @param g
	 *            the green component of the color
	 * @param b
	 *            the blue component of the color
	 * @param hsbvals
	 *            the array used to return the three HSB values, or
	 *            <code>null</code>
	 * @return an array of three elements containing the hue, saturation, and
	 *         brightness (in that order), of the color with the indicated red,
	 *         green, and blue components.
	 * @see java.awt.Color#getRGB()
	 * @see java.awt.Color#Color(int)
	 * @see java.awt.image.ColorModel#getRGBdefault()
	 * @since JDK1.0
	 */
	public static float[] RGBtoHSB(int r, int g, int b, float[] hsbvals) {
		float hue, saturation, brightness;
		if (hsbvals == null) {
			hsbvals = new float[3];
		}
		int cmax = (r > g) ? r : g;
		if (b > cmax)
			cmax = b;
		int cmin = (r < g) ? r : g;
		if (b < cmin)
			cmin = b;

		brightness = ((float) cmax) / 255.0f;
		if (cmax != 0)
			saturation = ((float) (cmax - cmin)) / ((float) cmax);
		else
			saturation = 0;
		if (saturation == 0)
			hue = 0;
		else {
			float redc = ((float) (cmax - r)) / ((float) (cmax - cmin));
			float greenc = ((float) (cmax - g)) / ((float) (cmax - cmin));
			float bluec = ((float) (cmax - b)) / ((float) (cmax - cmin));
			if (r == cmax)
				hue = bluec - greenc;
			else if (g == cmax)
				hue = 2.0f + redc - bluec;
			else
				hue = 4.0f + greenc - redc;
			hue = hue / 6.0f;
			if (hue < 0)
				hue = hue + 1.0f;
		}
		hsbvals[0] = hue;
		hsbvals[1] = saturation;
		hsbvals[2] = brightness;
		return hsbvals;
	}

	/**
	 * Creates a color in the specified <code>ColorSpace</code> with the color
	 * components specified in the <code>float</code> array and the specified
	 * alpha. The number of components is determined by the type of the
	 * <code>ColorSpace</code>. For example, RGB requires 3 components, but CMYK
	 * requires 4 components.
	 *
	 * @param cspace
	 *            the <code>ColorSpace</code> to be used to interpret the
	 *            components
	 * @param components
	 *            an arbitrary number of color components that is compatible
	 *            with the
	 * @param alpha
	 *            alpha value
	 * @throws IllegalArgumentException
	 *             if any of the values in the <code>components</code> array or
	 *             <code>alpha</code> is outside of the range 0.0 to 1.0
	 * @see #getComponents
	 * @see #getColorComponents public Color(ColorSpace cspace, float
	 *      components[], float alpha) { boolean rangeError = false; String
	 *      badComponentString = ""; int n = cspace.getNumComponents(); fvalue =
	 *      new float[n]; for (int i = 0; i < n; i++) { if (components[i] < 0.0
	 *      || components[i] > 1.0) { rangeError = true; badComponentString =
	 *      badComponentString + "Component " + i + " "; } else { fvalue[i] =
	 *      components[i]; } } if (alpha < 0.0 || alpha > 1.0) { rangeError =
	 *      true; badComponentString = badComponentString + "Alpha"; } else {
	 *      falpha = alpha; } if (rangeError) { throw new
	 *      IllegalArgumentException(
	 *      "Color parameter outside of expected range: " + badComponentString);
	 *      } frgbvalue = cspace.toRGB(fvalue); cs = cspace; value =
	 *      ((((int)(falpha*255)) & 0xFF) << 24) | ((((int)(frgbvalue[0]*255)) &
	 *      0xFF) << 16) | ((((int)(frgbvalue[1]*255)) & 0xFF) << 8) |
	 *      ((((int)(frgbvalue[2]*255)) & 0xFF) << 0); }
	 */

	/**
	 * Checks the color <code>float</code> components supplied for validity.
	 * Throws an <code>IllegalArgumentException</code> if the value is out of
	 * range.
	 *
	 * @param r
	 *            the Red component
	 * @param g
	 *            the Green component
	 * @param b
	 *            the Blue component
	 **/
	private static void testColorValueRange(float r, float g, float b, float a) {
		boolean rangeError = false;
		String badComponentString = "";
		if (a < 0.0 || a > 1.0) {
			rangeError = true;
			badComponentString = badComponentString + " Alpha";
		}
		if (r < 0.0 || r > 1.0) {
			rangeError = true;
			badComponentString = badComponentString + " Red";
		}
		if (g < 0.0 || g > 1.0) {
			rangeError = true;
			badComponentString = badComponentString + " Green";
		}
		if (b < 0.0 || b > 1.0) {
			rangeError = true;
			badComponentString = badComponentString + " Blue";
		}
		if (rangeError == true) {
			throw new IllegalArgumentException(
					"Color parameter outside of expected range:"
							+ badComponentString);
		}
	}

	/**
	 * Checks the color integer components supplied for validity. Throws an
	 * {@link IllegalArgumentException} if the value is out of range.
	 *
	 * @param r
	 *            the Red component
	 * @param g
	 *            the Green component
	 * @param b
	 *            the Blue component
	 **/
	private static void testColorValueRange(int r, int g, int b, int a) {
		boolean rangeError = false;
		String badComponentString = "";

		if (a < 0 || a > 255) {
			rangeError = true;
			badComponentString = badComponentString + " Alpha";
		}
		if (r < 0 || r > 255) {
			rangeError = true;
			badComponentString = badComponentString + " Red";
		}
		if (g < 0 || g > 255) {
			rangeError = true;
			badComponentString = badComponentString + " Green";
		}
		if (b < 0 || b > 255) {
			rangeError = true;
			badComponentString = badComponentString + " Blue";
		}
		if (rangeError == true) {
			throw new IllegalArgumentException(
					"Color parameter outside of expected range:"
							+ badComponentString);
		}
	}

	/**
	 * Creates an opaque sRGB color with the specified red, green, and blue
	 * values in the range (0.0 - 1.0). Alpha is defaulted to 1.0. The actual
	 * color used in rendering depends on finding the best match given the color
	 * space available for a particular output device.
	 *
	 * @throws IllegalArgumentException
	 *             if <code>r</code>, <code>g</code> or <code>b</code> are
	 *             outside of the range 0.0 to 1.0, inclusive
	 * @param r
	 *            the red component
	 * @param g
	 *            the green component
	 * @param b
	 *            the blue component
	 * @see #getRed
	 * @see #getGreen
	 * @see #getBlue
	 * @see #getRGB
	 */
	public Color(float r, float g, float b) {
		this((int) (r * 255 + 0.5), (int) (g * 255 + 0.5),
				(int) (b * 255 + 0.5));
		testColorValueRange(r, g, b, 1.0f);
		frgbvalue = new float[3];
		frgbvalue[0] = r;
		frgbvalue[1] = g;
		frgbvalue[2] = b;
		falpha = 1.0f;
		fvalue = frgbvalue;
	}

	/**
	 * Creates an sRGB color with the specified red, green, blue, and alpha
	 * values in the range (0.0 - 1.0). The actual color used in rendering
	 * depends on finding the best match given the color space available for a
	 * particular output device.
	 *
	 * @throws IllegalArgumentException
	 *             if <code>r</code>, <code>g</code> <code>b</code> or
	 *             <code>a</code> are outside of the range 0.0 to 1.0, inclusive
	 * @param r
	 *            the red component
	 * @param g
	 *            the green component
	 * @param b
	 *            the blue component
	 * @param a
	 *            the alpha component
	 * @see #getRed
	 * @see #getGreen
	 * @see #getBlue
	 * @see #getAlpha
	 * @see #getRGB
	 */
	public Color(float r, float g, float b, float a) {
		this((int) (r * 255 + 0.5), (int) (g * 255 + 0.5),
				(int) (b * 255 + 0.5), (int) (a * 255 + 0.5));
		frgbvalue = new float[3];
		frgbvalue[0] = r;
		frgbvalue[1] = g;
		frgbvalue[2] = b;
		falpha = a;
		fvalue = frgbvalue;
	}

	/**
	 * Creates an opaque sRGB color with the specified combined RGB value
	 * consisting of the red component in bits 16-23, the green component in
	 * bits 8-15, and the blue component in bits 0-7. The actual color used in
	 * rendering depends on finding the best match given the color space
	 * available for a particular output device. Alpha is defaulted to 255.
	 *
	 * @param rgb
	 *            the combined RGB components
	 * @see java.awt.image.ColorModel#getRGBdefault
	 * @see #getRed
	 * @see #getGreen
	 * @see #getBlue
	 * @see #getRGB
	 */
	public Color(int rgb) {
		value = 0xff000000 | rgb;
	}

	/**
	 * Creates an sRGB color with the specified combined RGBA value consisting
	 * of the alpha component in bits 24-31, the red component in bits 16-23,
	 * the green component in bits 8-15, and the blue component in bits 0-7. If
	 * the <code>hasalpha</code> argument is <code>false</code>, alpha is
	 * defaulted to 255.
	 *
	 * @param rgba
	 *            the combined RGBA components
	 * @param hasalpha
	 *            <code>true</code> if the alpha bits are valid;
	 *            <code>false</code> otherwise
	 * @see java.awt.image.ColorModel#getRGBdefault
	 * @see #getRed
	 * @see #getGreen
	 * @see #getBlue
	 * @see #getAlpha
	 * @see #getRGB
	 */
	public Color(int rgba, boolean hasalpha) {
		if (hasalpha) {
			value = rgba;
		} else {
			value = 0xff000000 | rgba;
		}
	}

	/**
	 * Creates an opaque sRGB color with the specified red, green, and blue
	 * values in the range (0 - 255). The actual color used in rendering depends
	 * on finding the best match given the color space available for a given
	 * output device. Alpha is defaulted to 255.
	 *
	 * @throws IllegalArgumentException
	 *             if <code>r</code>, <code>g</code> or <code>b</code> are
	 *             outside of the range 0 to 255, inclusive
	 * @param r
	 *            the red component
	 * @param g
	 *            the green component
	 * @param b
	 *            the blue component
	 * @see #getRed
	 * @see #getGreen
	 * @see #getBlue
	 * @see #getRGB
	 */
	public Color(int r, int g, int b) {
		this(r, g, b, 255);
	}

	/**
	 * Creates an sRGB color with the specified red, green, blue, and alpha
	 * values in the range (0 - 255).
	 *
	 * @throws IllegalArgumentException
	 *             if <code>r</code>, <code>g</code>, <code>b</code> or
	 *             <code>a</code> are outside of the range 0 to 255, inclusive
	 * @param r
	 *            the red component
	 * @param g
	 *            the green component
	 * @param b
	 *            the blue component
	 * @param a
	 *            the alpha component
	 * @see #getRed
	 * @see #getGreen
	 * @see #getBlue
	 * @see #getAlpha
	 * @see #getRGB
	 */
	public Color(int r, int g, int b, int a) {
		value = ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8)
				| ((b & 0xFF) << 0);
		testColorValueRange(r, g, b, a);
	}

	/**
	 * Creates a new <code>Color</code> that is a brighter version of this
	 * <code>Color</code>.
	 * <p>
	 * This method applies an arbitrary scale factor to each of the three RGB
	 * components of this <code>Color</code> to create a brighter version of
	 * this <code>Color</code>. Although <code>brighter</code> and
	 * <code>darker</code> are inverse operations, the results of a series of
	 * invocations of these two methods might be inconsistent because of
	 * rounding errors.
	 *
	 * @return a new <code>Color</code> object that is a brighter version of
	 *         this <code>Color</code>.
	 * @see java.awt.Color#darker
	 * @since JDK1.0
	 */
	public Color brighter() {
		int r = getRed();
		int g = getGreen();
		int b = getBlue();

		/*
		 * From 2D group: 1. black.brighter() should return grey 2. applying
		 * brighter to blue will always return blue, brighter 3. non pure color
		 * (non zero rgb) will eventually return white
		 */
		int i = (int) (1.0 / (1.0 - FACTOR));
		if (r == 0 && g == 0 && b == 0) {
			return new Color(i, i, i);
		}
		if (r > 0 && r < i)
			r = i;
		if (g > 0 && g < i)
			g = i;
		if (b > 0 && b < i)
			b = i;

		return new Color(Math.min((int) (r / FACTOR), 255), Math.min(
				(int) (g / FACTOR), 255), Math.min((int) (b / FACTOR), 255));
	}

	/**
	 * Creates a new <code>Color</code> that is a darker version of this
	 * <code>Color</code>.
	 * <p>
	 * This method applies an arbitrary scale factor to each of the three RGB
	 * components of this <code>Color</code> to create a darker version of this
	 * <code>Color</code>. Although <code>brighter</code> and
	 * <code>darker</code> are inverse operations, the results of a series of
	 * invocations of these two methods might be inconsistent because of
	 * rounding errors.
	 *
	 * @return a new <code>Color</code> object that is a darker version of this
	 *         <code>Color</code>.
	 * @see java.awt.Color#brighter
	 * @since JDK1.0
	 */
	public Color darker() {
		return new Color(Math.max((int) (getRed() * FACTOR), 0), Math.max(
				(int) (getGreen() * FACTOR), 0), Math.max(
				(int) (getBlue() * FACTOR), 0));
	}

	/**
	 * Determines whether another object is equal to this <code>Color</code>.
	 * <p>
	 * The result is <code>true</code> if and only if the argument is not
	 * <code>null</code> and is a <code>Color</code> object that has the same
	 * red, green, blue, and alpha values as this object.
	 *
	 * @param obj
	 *            the object to test for equality with this <code>Color</code>
	 * @return <code>true</code> if the objects are the same; <code>false</code>
	 *         otherwise.
	 * @since JDK1.0
	 */
	public boolean equals(Object obj) {
		return obj instanceof Color && ((Color) obj).value == this.value;
	}

	/**
	 * Returns the alpha component in the range 0-255.
	 *
	 * @return the alpha component.
	 * @see #getRGB
	 */
	public int getAlpha() {
		return (getRGB() >> 24) & 0xff;
	}

	/**
	 * Returns the blue component in the range 0-255 in the default sRGB space.
	 *
	 * @return the blue component.
	 * @see #getRGB
	 */
	public int getBlue() {
		return (getRGB() >> 0) & 0xFF;
	}

	/**
	 * Returns a <code>float</code> array containing only the color components
	 * of the <code>Color</code>, in the <code>ColorSpace</code> of the
	 * <code>Color</code>. If <code>compArray</code> is <code>null</code>, an
	 * array with length equal to the number of components in the associated
	 * <code>ColorSpace</code> is created for the return value. Otherwise,
	 * <code>compArray</code> must have at least this length and it is filled in
	 * with the components and returned.
	 *
	 * @param compArray
	 *            an array that this method fills with the color components of
	 *            this <code>Color</code> in its <code>ColorSpace</code> and
	 *            returns
	 * @return the color components in a <code>float</code> array.
	 */
	public float[] getColorComponents(float[] compArray) {
		if (fvalue == null)
			return getRGBColorComponents(compArray);
		float[] f;
		int n = fvalue.length;
		if (compArray == null) {
			f = new float[n];
		} else {
			f = compArray;
		}
		for (int i = 0; i < n; i++) {
			f[i] = fvalue[i];
		}
		return f;
	}

	/**
	 * Returns a <code>float</code> array containing the color and alpha
	 * components of the <code>Color</code>, in the <code>ColorSpace</code> of
	 * the <code>Color</code>. If <code>compArray</code> is <code>null</code>,
	 * an array with length equal to the number of components in the associated
	 * <code>ColorSpace</code> plus one is created for the return value.
	 * Otherwise, <code>compArray</code> must have at least this length and it
	 * is filled in with the components and returned.
	 *
	 * @param compArray
	 *            an array that this method fills with the color and alpha
	 *            components of this <code>Color</code> in its
	 *            <code>ColorSpace</code> and returns
	 * @return the color and alpha components in a <code>float</code> array.
	 */
	public float[] getComponents(float[] compArray) {
		if (fvalue == null)
			return getRGBComponents(compArray);
		float[] f;
		int n = fvalue.length;
		if (compArray == null) {
			f = new float[n + 1];
		} else {
			f = compArray;
		}
		for (int i = 0; i < n; i++) {
			f[i] = fvalue[i];
		}
		f[n] = falpha;
		return f;
	}

	/**
	 * Returns the green component in the range 0-255 in the default sRGB space.
	 *
	 * @return the green component.
	 * @see #getRGB
	 */
	public int getGreen() {
		return (getRGB() >> 8) & 0xFF;
	}

	/**
	 * Returns the red component in the range 0-255 in the default sRGB space.
	 *
	 * @return the red component.
	 * @see #getRGB
	 */
	public int getRed() {
		return (getRGB() >> 16) & 0xFF;
	}

	/**
	 * Returns the RGB value representing the color in the default sRGB
	 * {@link ColorModel}. (Bits 24-31 are alpha, 16-23 are red, 8-15 are green,
	 * 0-7 are blue).
	 *
	 * @return the RGB value of the color in the default sRGB
	 *         <code>ColorModel</code>.
	 * @see java.awt.image.ColorModel#getRGBdefault
	 * @see #getRed
	 * @see #getGreen
	 * @see #getBlue
	 * @since JDK1.0
	 */
	public int getRGB() {
		return value;
	}

	/**
	 * Returns a <code>float</code> array containing only the color components
	 * of the <code>Color</code>, in the default sRGB color space. If
	 * <code>compArray</code> is <code>null</code>, an array of length 3 is
	 * created for the return value. Otherwise, <code>compArray</code> must have
	 * length 3 or greater, and it is filled in with the components and
	 * returned.
	 *
	 * @param compArray
	 *            an array that this method fills with color components and
	 *            returns
	 * @return the RGB components in a <code>float</code> array.
	 */
	public float[] getRGBColorComponents(float[] compArray) {
		float[] f;
		if (compArray == null) {
			f = new float[3];
		} else {
			f = compArray;
		}
		if (frgbvalue == null) {
			f[0] = ((float) getRed()) / 255f;
			f[1] = ((float) getGreen()) / 255f;
			f[2] = ((float) getBlue()) / 255f;
		} else {
			f[0] = frgbvalue[0];
			f[1] = frgbvalue[1];
			f[2] = frgbvalue[2];
		}
		return f;
	}

	/**
	 * Returns a <code>float</code> array containing the color and alpha
	 * components of the <code>Color</code>, as represented in the default sRGB
	 * color space. If <code>compArray</code> is <code>null</code>, an array of
	 * length 4 is created for the return value. Otherwise,
	 * <code>compArray</code> must have length 4 or greater, and it is filled in
	 * with the components and returned.
	 *
	 * @param compArray
	 *            an array that this method fills with color and alpha
	 *            components and returns
	 * @return the RGBA components in a <code>float</code> array.
	 */
	public float[] getRGBComponents(float[] compArray) {
		float[] f;
		if (compArray == null) {
			f = new float[4];
		} else {
			f = compArray;
		}
		if (frgbvalue == null) {
			f[0] = ((float) getRed()) / 255f;
			f[1] = ((float) getGreen()) / 255f;
			f[2] = ((float) getBlue()) / 255f;
			f[3] = ((float) getAlpha()) / 255f;
		} else {
			f[0] = frgbvalue[0];
			f[1] = frgbvalue[1];
			f[2] = frgbvalue[2];
			f[3] = falpha;
		}
		return f;
	}

	// REMIND: this should really be a Ref!
	/**
	 * The paint context used to generate a solid color pattern.
	 *
	 * @see createContext()
	 *
	 *      transient private PaintContext theContext;
	 */
	/**
	 * Creates and returns a {@link PaintContext} used to generate a solid color
	 * pattern. This enables a <code>Color</code> object to be used as an
	 * argument to any method requiring an object implementing the {@link Paint}
	 * interface. The same <code>PaintContext</code> is returned, regardless of
	 * whether or not <code>r</code>, <code>r2d</code>, <code>xform</code>, or
	 * <code>hints</code> are <code>null</code>.
	 *
	 * @param cm
	 *            the specified <code>ColorModel</code>
	 * @param r
	 *            the specified {@link Rectangle}
	 * @param r2d
	 *            the specified {@link Rectangle2D}
	 * @param xform
	 *            the specified {@link AffineTransform}
	 * @param hints
	 *            the specified {@link RenderingHints}
	 * @return a <code>PaintContext</code> that is used to generate a solid
	 *         color pattern.
	 * @see Paint
	 * @see PaintContext
	 * @see Graphics2D#setPaint public synchronized PaintContext
	 *      createContext(ColorModel cm, Rectangle r, Rectangle2D r2d,
	 *      AffineTransform xform, RenderingHints hints) { PaintContext pc =
	 *      theContext; if (pc == null) { pc = new ColorPaintContext(value, cm);
	 *      theContext = pc; } return pc; }
	 */
	/**
	 * Returns the transparency mode for this <code>Color</code>. This is
	 * required to implement the <code>Paint</code> interface.
	 *
	 * @return this <code>Color</code> object's transparency mode.
	 * @see Paint
	 * @see Transparency
	 * @see #createContext
	 */
	public int getTransparency() {
		int alpha = getAlpha();
		if (alpha == 0xff) {
			return Transparency.OPAQUE;
		} else if (alpha == 0) {
			return Transparency.BITMASK;
		} else {
			return Transparency.TRANSLUCENT;
		}
	}

	/**
	 * Computes the hash code for this <code>Color</code>.
	 *
	 * @return a hash code value for this object.
	 * @since JDK1.0
	 */
	public int hashCode() {
		return value;
	}

	/**
	 * Returns a <code>float</code> array containing the color and alpha
	 * components of the <code>Color</code>, in the <code>ColorSpace</code>
	 * specified by the <code>cspace</code> parameter. If <code>compArray</code>
	 * is <code>null</code>, an array with length equal to the number of
	 * components in <code>cspace</code> plus one is created for the return
	 * value. Otherwise, <code>compArray</code> must have at least this length,
	 * and it is filled in with the components and returned.
	 *
	 * @param cspace
	 *            a specified <code>ColorSpace</code>
	 * @param compArray
	 *            an array that this method fills with the color and alpha
	 *            components of this <code>Color</code> in the specified
	 *            <code>ColorSpace</code> and returns
	 * @return the color and alpha components in a <code>float</code> array.
	 *
	 *         public float[] getComponents(ColorSpace cspace, float[]
	 *         compArray) { if (cs == null) { cs =
	 *         ColorSpace.getInstance(ColorSpace.CS_sRGB); } float f[]; if
	 *         (fvalue == null) { f = new float[3]; f[0] =
	 *         ((float)getRed())/255f; f[1] = ((float)getGreen())/255f; f[2] =
	 *         ((float)getBlue())/255f; } else { f = fvalue; } float tmp[] =
	 *         cs.toCIEXYZ(f); float tmpout[] = cspace.fromCIEXYZ(tmp); if
	 *         (compArray == null) { compArray = new float[tmpout.length + 1]; }
	 *         for (int i = 0 ; i < tmpout.length ; i++) { compArray[i] =
	 *         tmpout[i]; } if (fvalue == null) { compArray[tmpout.length] =
	 *         ((float)getAlpha())/255f; } else { compArray[tmpout.length] =
	 *         falpha; } return compArray; }
	 */

	/**
	 * Returns a <code>float</code> array containing only the color components
	 * of the <code>Color</code> in the <code>ColorSpace</code> specified by the
	 * <code>cspace</code> parameter. If <code>compArray</code> is
	 * <code>null</code>, an array with length equal to the number of components
	 * in <code>cspace</code> is created for the return value. Otherwise,
	 * <code>compArray</code> must have at least this length, and it is filled
	 * in with the components and returned.
	 *
	 * @param cspace
	 *            a specified <code>ColorSpace</code>
	 * @param compArray
	 *            an array that this method fills with the color components of
	 *            this <code>Color</code> in the specified
	 *            <code>ColorSpace</code>
	 * @return the color components in a <code>float</code> array.
	 *
	 *         public float[] getColorComponents(ColorSpace cspace, float[]
	 *         compArray) { if (cs == null) { cs =
	 *         ColorSpace.getInstance(ColorSpace.CS_sRGB); } float f[]; if
	 *         (fvalue == null) { f = new float[3]; f[0] =
	 *         ((float)getRed())/255f; f[1] = ((float)getGreen())/255f; f[2] =
	 *         ((float)getBlue())/255f; } else { f = fvalue; } float tmp[] =
	 *         cs.toCIEXYZ(f); float tmpout[] = cspace.fromCIEXYZ(tmp); if
	 *         (compArray == null) { return tmpout; } for (int i = 0 ; i <
	 *         tmpout.length ; i++) { compArray[i] = tmpout[i]; } return
	 *         compArray; }
	 */

	/**
	 * Returns the <code>ColorSpace</code> of this <code>Color</code>.
	 *
	 * @return this <code>Color</code> object's <code>ColorSpace</code>.
	 *
	 *         public ColorSpace getColorSpace() { if (cs == null) { cs =
	 *         ColorSpace.getInstance(ColorSpace.CS_sRGB); } return cs; }
	 */

	/**
	 * Returns a string representation of this <code>Color</code>. This method
	 * is intended to be used only for debugging purposes. The content and
	 * format of the returned string might vary between implementations. The
	 * returned string might be empty but cannot be <code>null</code>.
	 *
	 * @return a string representation of this <code>Color</code>.
	 */
	public String toString() {
		return getClass().getName() + "[r=" + getRed() + ",g=" + getGreen()
				+ ",b=" + getBlue() + "]";
	}

	public String toHexString(){
		double a = (1.0 / 255.0 * getAlpha());
		return "rgba(" + getRed() + "," + getGreen() + "," + getBlue() + "," + a + ")";
	}



}
